package org.ns.framework.datasource.mybatis;

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 事务管理
 * @author zhangxingyu
 */
public class TransactonManage implements AutoCloseable {
	
    private final static ThreadLocal<TransactonManage> threadLocal = new ThreadLocal<>();

    Method method;	// 创建事务的开始方法.

    ExecutorType executorType; // 事务执行类型 SIMPLE, REUSE, BATCH

    TransactionIsolationLevel level; // 事物级别

    Map<String, SqlSession> sqlSessionMap = new HashMap<>();



    private TransactonManage(Method method, ExecutorType executorType, TransactionIsolationLevel level) {
        this.method = method;
        this.executorType = executorType;
        this.level = level;
    }

    /**
     *
     * @param executorType SIMPLE, REUSE, BATCH(批处理)
     * @return
     */
    public static TransactonManage create(ExecutorType executorType) {
        return create(null, executorType, null);
    }

    public static TransactonManage create() {
        return create(null, null, null);
    }

    public static TransactonManage create(Method method, ExecutorType executorType, TransactionIsolationLevel level) {

        TransactonManage transactonManage = threadLocal.get();
        if (transactonManage == null) {
            transactonManage = new TransactonManage(method, executorType, level);
            threadLocal.set(transactonManage);
        }
        return transactonManage;
    }


    public static TransactonManage get() {
        return threadLocal.get();
    }

    public SqlSession openSession(String dataSourceName, SqlSessionFactory sqlSessionFactory) {
        SqlSession sqlSession = sqlSessionMap.get(dataSourceName);
        if (sqlSession != null) {
            return sqlSession;
        }
        if (executorType == null) {
            sqlSession = sqlSessionFactory.openSession(ExecutorType.SIMPLE, level);
        } else {
            sqlSession = sqlSessionFactory.openSession(executorType, level);
        }
        sqlSessionMap.put(dataSourceName, sqlSession);
        return sqlSession;
    }


    public CommitResult commit() {
        CommitResult commitResult = new CommitResult();
        for (Map.Entry<String, SqlSession> entry : sqlSessionMap.entrySet()) {
            SqlSession sqlSession = entry.getValue();
            try {
                sqlSession.commit(true);
            } catch (Exception e) {
                e.printStackTrace();
                commitResult.addFailDataSource(entry.getKey());
            }


        }
        return commitResult;
    }
    public boolean commit(String dataSourceName) {
        SqlSession sqlSession = sqlSessionMap.get(dataSourceName);

        if (sqlSession != null) {
            try {
                sqlSession.commit(true);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    public boolean rallback(String dataSourceName) {
        SqlSession sqlSession = sqlSessionMap.get(dataSourceName);

        if (sqlSession != null) {
            try {
                sqlSession.rollback(true);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    public void rallback() {
        for (Map.Entry<String, SqlSession> entry : sqlSessionMap.entrySet()) {
            SqlSession sqlSession = entry.getValue();
            try {
                sqlSession.rollback(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void close(Method method) {

        if (method != this.method) {
            return;
        }
        try {
	        rallback();
	        for (Map.Entry<String, SqlSession> entry : sqlSessionMap.entrySet()) {
	            SqlSession sqlSession = entry.getValue();
	            try {
	                sqlSession.close();
	            } catch (Exception e) {
	                e.printStackTrace();
	            }
	        }
	        sqlSessionMap.clear();
        } finally {
        	threadLocal.remove();
        }
    }

    @Override
    public void close() {

        close(null);
    }
}
